﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Image Processing in the Context of a Visual Model
    /// T. G. Stockham
    /// 
    /// TODO: more clarity to implementation with original paper
    /// </summary>
    public sealed class Stockham : Operator
    {
        protected override void Run(float[][,] mapInput, int width, int height, ref float[][,] mapOutput)
        {

            ConvertRGBToYxy(mapInput, ref mapOutput);
            Tonemap(mapOutput);
            ConvertYxyToRGB(mapOutput);
        }

        protected override void SetParam(string param, string value)
        {

            // not implemented
        }

        private void ConvertRGBToYxy(float[][,] mapInput, ref float[][,] mapOutput)
        {

            int width = mapInput[0].GetLength(0);
            int height = mapInput[0].GetLength(1);

            int x = 0, y = 0;
            double R = 0, G = 0, B = 0, X = 0, Y = 0, Z = 0, Y_ = 0, x_ = 0, y_ = 0;

            ColorSpaceConverter converter = new ColorSpaceConverter();

            for (y = 0; y < height; y++)
                for (x = 0; x < width; x++)
                {

                    R = mapInput[2][x, y];
                    G = mapInput[1][x, y];
                    B = mapInput[0][x, y];

                    converter.RGBToXYZ(R, G, B, ref X, ref Y, ref Z);
                    converter.XYZToYxy(X, Y, Z, ref Y_, ref x_, ref y_);

                    mapOutput[0][x, y] = (float)Y_;
                    mapOutput[1][x, y] = (float)x_;
                    mapOutput[2][x, y] = (float)y_;
                }
        }

        private void Tonemap(float[][,] map)
        {

            int width = map[0].GetLength(0);
            int height = map[0].GetLength(1);

            int x = 0, y = 0;
            float val = 0;
            double Lmax = 0;

            for (y = 0; y < height; y++)
                for (x = 0; x < width; x++)
                {

                    val = map[0][x, y];

                    if (val > Lmax)
                        Lmax = val;
                }

            for (y = 0; y < height; y++)
                for (x = 0; x < width; x++)
                {

                    val = map[0][x, y];
                    map[0][x, y] = (float)(Math.Log(val + 1) / Math.Log(Lmax + 1));
                }
        }

        private void ConvertYxyToRGB(float[][,] map)
        {

            int width = map[0].GetLength(0);
            int height = map[0].GetLength(1);

            int x = 0, y = 0;
            double Y_ = 0, x_ = 0, y_ = 0, X = 0, Y = 0, Z = 0, R = 0, G = 0, B = 0;

            ColorSpaceConverter converter = new ColorSpaceConverter();

            for (y = 0; y < height; y++)
                for (x = 0; x < width; x++)
                {

                    Y_ = map[0][x, y];
                    x_ = map[1][x, y];
                    y_ = map[2][x, y];

                    converter.YxyToXYZ(Y_, x_, y_, ref X, ref Y, ref Z);
                    converter.XYZToRGB(X, Y, Z, ref R, ref G, ref B);

                    map[2][x, y] = (float)Math.Min(Math.Max(R, 0), 1);
                    map[1][x, y] = (float)Math.Min(Math.Max(G, 0), 1);
                    map[0][x, y] = (float)Math.Min(Math.Max(B, 0), 1);
                }
        }
    }
}
